package net.pm.common.dao.impl;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;

import javax.annotation.Resource;
import javax.persistence.Id;

import net.pm.common.dao.GenericDao;
import net.pm.common.toolkit.GenericsUtils;
import net.pm.common.toolkit.Pager;
import net.pm.common.toolkit.StringUtils;

import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

/**公共的数据访问接口实现
 *
 * @param <T>
 * @param <ID>
 */
@SuppressWarnings({"unchecked"})
public abstract class GenericDaoImpl<T extends Serializable ,ID extends Serializable> implements GenericDao<T, ID> {

	@Resource(name="sessionFactory")
	private SessionFactory sessionFactory;
	
	public Session getSession(){
		return sessionFactory.getCurrentSession();
	}
	private final Class<T> entityClass;				//T类的class
	private String idName = null;					//T类的idName
    private final String LIST_ALL_JPQL;				//查询所有
    private final String COUNT_ALL_JPQL;			//查询总记录数

    public GenericDaoImpl() {
		super();
		  this.entityClass = (Class<T>) GenericsUtils.getSuperClassGenricType(this.getClass());
	        /*标在field上可用
	        Field[] fields = this.entityClass.getDeclaredFields();
	        for(Field field : fields) {
	            if(field.isAnnotationPresent(Id.class)) {
	                this.idName = field.getName();
	            }
	        }*/
		    //标注在getter方法上
	        Method[] methods = this.entityClass.getDeclaredMethods();
	        for(Method method : methods){
	        	if(method.getName().startsWith("get")){
	        		if(method.isAnnotationPresent(Id.class)){
	        			String temp = method.getName().replaceAll("^get", "");
	        				//将第一个字母变成小写
		        		this.idName = StringUtils.firstLetterToLower(temp);
		        		break;
		        	}
	        	}
	        }
	        
	        String entityClassName = this.entityClass.getSimpleName();
	        //TODO @Entity name not null
	        LIST_ALL_JPQL = "from " + entityClassName + " e order by e." + idName + " asc";
	        COUNT_ALL_JPQL = " select count(e."+idName+") from " + entityClassName+" e";
		
	}


	@Override
	public ID create(final T t) {
		return (ID) this.getSession().save(t);
	}

	@Override
	public void batchCreate(final Collection<T> ts) {
		final Session session = this.getSession();
		int i = 1;
		for(T t : ts){
			i++;
			session.save(t);
			if(i%20 == 0)
				session.flush();
		}
	}

	@Override
	public void remove(final T t) {
		this.getSession().delete(t);
	}

	@Override
	public void removeById(final ID id) {
		this.getSession().delete(this.findEntityById(id));
	}

	@Override
	public Integer removeByJpql(final StringBuilder jpql,final Object... args) {
		Query query = this.getSession().createQuery(jpql.toString());
		this.setParameters(query, args);
		return query.executeUpdate();
	}

	@Override
	public void batchRemove(final Collection<ID> ids) {
		final Session session = this.getSession();
		int i = 1;
		for(ID id : ids){
			i++;
			session.delete(this.findEntityById(id));
			if(i%20 == 0)
				session.flush();
		}
	}

	@Override
	public void modify(final T t) {
		this.getSession().merge(t);
	}

	@Override
	public Integer modifyByJpql(final StringBuilder jpql,final Object... args) {
		Query query = this.getSession().createQuery(jpql.toString());
		this.setParameters(query, args);
		return query.executeUpdate();
	}

	@Override
	public void batchModify(final Collection<T> ts) {
		final Session session = this.getSession();
		int i = 1;
		for(T t : ts){
			i++;
			session.merge(t);
			if(i%20 == 0)
				session.flush();
		}
	}

	@Override
	public void createOrModify(final T t) {
		this.getSession().saveOrUpdate(t);
	}
	
	@Override
	public T loadEntityById(ID id) {
		return (T) this.getSession().load(entityClass, id);
	}

	@Override
	public T findEntityById(final ID id) {
		return (T) this.getSession().get(entityClass,id);
	}

	@Override
	public List<T> findEntitiesByJpql(final StringBuilder jpql,final Object... args) {
		Query query = this.getSession().createQuery(jpql.toString());
		this.setParameters(query, args);
		return query.list();
	}
	
	@Override
	public T findUniqueEntityByJpql(final StringBuilder jpql,final Object... args){
		Query query = this.getSession().createQuery(jpql.toString());
		this.setParameters(query, args);
		return (T) query.setMaxResults(1).uniqueResult();
	}

	@Override
	public Pager<T> findPagerByJpql(final Integer pagesize,final Integer pageno,
			final StringBuilder jpql,final Object... args) {
		Query query  = this.getSession().createQuery(jpql.toString());
		this.setParameters(query, args);
		
		List<T> result = query.list();
		//计算总记录数
		Long totalsize = (long) result.size();
		
		query.setFirstResult(pagesize*(pageno-1));
		query.setMaxResults(pagesize);
		
		result = query.list();
		//计算总页数
		Integer pagecount = (totalsize.intValue()+pagesize-1)/pagesize;
		
		return new Pager<T>(pageno, pagesize, pagecount, totalsize, result);
	}

	@Override
	public List<T> findAll() {
		return this.getSession().createQuery(LIST_ALL_JPQL).list();
	}
	
	@Override
	public Long findCountAll() {
		return (Long) this.getSession().createQuery(COUNT_ALL_JPQL)
				.uniqueResult();
	}
	
	@Override
	public Long findCountByJpql(final StringBuilder jpql,final Object... args){
		Query query = this.getSession().createQuery(jpql.toString());
		this.setParameters(query, args);
		return (Long) query.uniqueResult();
	}
	
	@Override
	public List<?> CallProcedure(String procName) {
		final Session session = this.getSession();
		String proc = "{call " + procName + "}";
		SQLQuery query = session.createSQLQuery(proc);

		List<?> list = query.list();
		return list;
	}
	
	@Override
	public void commit() {
		this.getSession().flush();
		this.getSession().clear();
	}

	/**设置参数
	 * @param query
	 * @param params
	 */
	private void setParameters(Query query,Object[] params){
		int len = params.length;
		for(int i = 0;i<len;i++){
			query.setParameter(i, params[i]);
		}
	}
	
}
